#include <stdio.h>
#include <stdlib.h>
#include "hj_log.h"
#include <time.h>
#include <string.h>

#if EN_SAVE_TO_FILE
#include <signal.h>
#endif

#if EN_CACHE_BUFFER
#define CACHE_BUFFER_LEN 1024
static char out_buffer[CACHE_BUFFER_LEN] = {0};
#endif

static void user_log_print(int level, const char *timestamp, const char *file, const char *func,
                    int line, const char *fmt, va_list args);
static void hj_log_print1(int level, const char *timestamp, const char *file, const char *func, \
                    int line, const char *fmt, va_list args);
static void hj_log_print2(int level, const char *timestamp, const char *file, const char *func, \
                    int line, const char *fmt, va_list args);

static log_conf_t log_conf = {
    .level = HJ_LOG_LEVEL_USER,
    .log_print = hj_log_print1,
    .user_log_print = user_log_print
};

static int color[8] = {HJ_LOG_RED, HJ_LOG_RED, HJ_LOG_YELLOW, HJ_LOG_DARK_GREEN, HJ_LOG_GREEN,
                       HJ_LOG_DARK_GREEN, HJ_LOG_WHITE, HJ_LOG_WHITE};
static char *log_str[] = {
    "[FATAL]", "[ERROR]", "[WARNING]", "[INFO]", "[DEBUG]", "[TRACE]"
};

void hj_log_print(int level, const char *timestamp, const char *file, const char *func,
                  int line, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    log_conf.log_print(level, timestamp, file, func, line, fmt, args);
    va_end(args);
}


int file_name_create(char *new_name, char *old_name)
{
    if (new_name == NULL || old_name == NULL)
    {
        return 1;
    }

    sscanf(old_name, "%[^.]", new_name);
    strcat(new_name, "-");
    strcat(new_name, __TIMESTAMP__);
    strcat(new_name, strchr(old_name, '.'));

    return 0;
}

#if EN_SAVE_TO_FILE
static void signal_handler (int signo)
{
    if (signo == SIGINT)
    {
        printf("Caught SIGINT!\n");
        exit(EXIT_FAILURE);
    }
    else if (signo == SIGTERM)
        printf("Caught SIGTERM!\n");
    else {
        fprintf(stderr, "Unexpected signal!\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

static void close_log_file(void)
{
    fclose(log_conf.fp);
}

static void open_log_file(void)
{
    memset(log_conf.log_file, 0, sizeof(log_conf.log_file));
    file_name_create(log_conf.log_file, HJ_LOG_FILE_NAME);
    
    log_conf.fp = fopen(log_conf.log_file, "a+b");
    fseek(log_conf.fp, 0, SEEK_END);

    atexit(close_log_file);
    if (signal (SIGINT, signal_handler) == SIG_ERR)
    {
        fprintf(stderr, "Cannot handle SIGINT!\n");
        exit(EXIT_FAILURE);
    }
    if (signal (SIGTERM, signal_handler) == SIG_ERR)
    {
        fprintf(stderr, "Cannot handle SIGTERM!\n");
        exit(EXIT_FAILURE);
    }
}
#endif

static void hj_log_print1(int level, const char *timestamp, const char *file, const char *func, \
                            int line, const char *fmt, va_list args)
{
    #if EN_SAVE_TO_FILE
    open_log_file();
    #endif

    log_conf.log_print = hj_log_print2;
    log_conf.log_print(level, timestamp, file, func, line, fmt, args);
}

static void hj_log_print2(int level, const char *timestamp, const char *file, const char *func, \
                            int line, const char *fmt, va_list args)
{
    #if EN_SAVE_TO_FILE
    long size = ftell(log_conf.fp);
    if (size > HJ_LOG_FILE_SIZE)
    {
        fclose(log_conf.fp);
        open_log_file();
    }
    #endif

    if (log_conf.level >= level && log_conf.user_log_print)
    {
        log_conf.user_log_print(level, timestamp, file, func, line, fmt, args);
    }
}

char *hj_get_timestamp(void)
{
    time_t rawtime;
    struct tm *info;
    static char buffer[80];
    time(&rawtime);
    info = localtime(&rawtime);
    strftime(buffer, 80, "%Y%m%d-%H:%M:%S", info);
    return buffer;
}

static void user_log_print(int level, const char *time_stamp, const char *file, const char *func,
                  int line, const char *fmt, va_list args)
{
    FILE *fp = stdout;
    #if EN_SAVE_TO_FILE
    fp = log_conf.fp;
    #endif

    #if EN_SHOW_COLOR
    fprintf(fp, "\033[40;%dm%s\033[0m %s %s:%d ", color[level], log_str[level], time_stamp, func, line);
    #else
    fprintf(fp, "%s%s %s:%d ", log_str[level], time_stamp, func, line);
    #endif

    vfprintf(fp, fmt, args);

    #if EN_SHOW_COLOR
    fprintf(fp, "\n");
    #else
    fprintf(fp, "\n");
    #endif
}
